/*
 *	This file is part of Lounge-Cast.
 *
 *  Author:  	Markus Moormann
 *	E-mail:  	mmoorman@uni-osnabrueck.de
 *	Copyright:	University of Osnabrueck (Zentrum Virtuos)
 * 	Website:	www.uni-osnabrueck.de
 *	Date:		16.02.2011
 *
 *	Lounge-Cast is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *	Lounge-Cast is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Lounge-Cast.  If not, see <http://www.gnu.org/licenses/>.
 */
package webservice.functions;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collection;
import java.util.StringTokenizer;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.ProduceMime;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;

import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.MultipartStream.MalformedStreamException;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.util.Streams;
import org.apache.log4j.Logger;

import webservice.dbConnection.ConnectionHelper;
import webservice.helper.HelperClass;

import com.twmacinta.util.MD5;

import eu.medsea.mimeutil.MimeUtil;


/**
 * WebService class which can upload a file to server
 * the only function that is not used by the client but by the frontend 
 * 
 * @author Markus Moormann
 */
@Path("/upload")
@ProduceMime("text/html")
public class UploadResource {
	
	/**
	 * size of reading buffer
	 */
	private static final int BUFF_SIZE = 1000000;
	/**
	 * reading buffer
	 */
	private static final byte[] buffer = new byte[BUFF_SIZE];
	/**
	 * a logger to log errors, debug information, etc.
	 */
	private static Logger LOG = Logger.getLogger(UploadResource.class);
	/**
	 * System seperator
	 */
	private String sep = File.separator;
	/**
	 * indicates whether MimeDetector has already been set
	 */
	private static boolean isMimeDetectorRegistered = false;
	
	/**
	 * upload a file to server
	 * called as http://example.org/webservice/uploadedFiles/upload?user_id=&type=
	 * as POST
	 * 
	 * @param request the request containing the file and further information
	 * @return the URL pointing to the file
	 */
	@POST
	public String upload(@Context HttpServletRequest request) {
		ServletFileUpload upload = new ServletFileUpload();
		String filename = "";
		FileItemIterator iter;
		
		MD5 md5 = null;
		
		String user_id = "";
		String type = "";
		
		String subdir = "";
		
		String url = HelperClass.getProperty("webserviceURL") + "/uploadedFiles/files/";
		String insertFileQuery = "";
		
		File uploadFile;
		LOG.debug("uploading new file");
		
		//use MimiUtil class for detecting MIME type
		//it's important for later download, so that OSMF can handle the type correctly
		if(!UploadResource.isMimeDetectorRegistered){
			MimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.MagicMimeMimeDetector");
			MimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.ExtensionMimeDetector");
			UploadResource.isMimeDetectorRegistered = true;
		}
		Connection connection = null;
		Statement statement = null;
		try {
			iter = upload.getItemIterator(request);
			while (iter.hasNext()) {
				FileItemStream item = iter.next();
				InputStream stream = item.openStream();
				//check for form item may be 'user_id' of 'type'
				if (item.isFormField()) {
					String fieldName = item.getFieldName();
					if(fieldName.equals("user_id")) {
						user_id = Streams.asString(stream);
					} else if(fieldName.equals("type")) {
						type = Streams.asString(stream);
					}
				} else {
					StringTokenizer tok = new StringTokenizer(item.getName(), ".");
					String extension = "";
					//get extension of file
					while(tok.hasMoreTokens()) {
						extension = tok.nextToken();
					}
					//get MD5 hash of filename
					md5 = new MD5();
					md5.Update(item.getName() + System.currentTimeMillis());
					filename = md5.asHex() + "." + extension;
					
					//if filename ends with ppt for Powerpoint put it in another directory
					if(item.getName().endsWith(".ppt")) {
						subdir = HelperClass.getProperty("slideSubFolder");
						//create slide subdirectory if it doesn't exist
						File sub = new File(HelperClass.getProperty("generalUploadFolder") + sep + subdir);
						if(!sub.exists() || !sub.isDirectory()) {
							sub.mkdirs();
						}
					}
					//put videos in video subdir
					if(item.getName().endsWith(".mp4") || item.getName().endsWith(".flv")) {
						subdir = HelperClass.getProperty("videoSubFolder");
						//create video subdirectory if it doesn't exist
						File sub = new File(HelperClass.getProperty("videoSubFolder") + sep + subdir);
						if(!sub.exists() || !sub.isDirectory()) {
							sub.mkdirs();
						}
						url = HelperClass.getProperty("lighttpd");
					}
					//put pictures in pictures subdir
					if( item.getName().endsWith(".jpg") || 
						item.getName().endsWith(".gif") || 
						item.getName().endsWith(".png")) {
						subdir = HelperClass.getProperty("pictureSubFolder");
						//create pictures subdirectory if it doesn't exist
						File sub = new File(HelperClass.getProperty("pictureSubFolder") + sep + subdir);
						if(!sub.exists() || !sub.isDirectory()) {
							sub.mkdirs();
						}
					}
					uploadFile = new File(
							HelperClass.getProperty("generalUploadFolder") + (subdir.equals("") ? "" : sep + subdir) + sep + filename + sep);
					String path = uploadFile.getAbsolutePath().replace("\\", "/");
					FileOutputStream fout = new FileOutputStream(uploadFile);
					//read the file from request and write it to a local file
					while (true) {
						synchronized (buffer) {
							int amountRead = stream.read(buffer);
							if (amountRead == -1) {
								break;
							}
							fout.write(buffer, 0, amountRead);
						}
					}
					
			        Collection<?> mimeTypes = MimeUtil.getMimeTypes(uploadFile);
			        //increasing the possibility to get a more specific MimeType
			        LOG.debug(MimeUtil.getMostSpecificMimeType(mimeTypes));
			        
					connection = ConnectionHelper.getConnection();
					statement = connection.createStatement();
					insertFileQuery = 	"INSERT INTO " +
											"`uploaded_files` " +
											"(" +
												"`file_id`,"	  +			
												"`url`," 		  +
												"`type`," 		  +
												"`uploaded_by`,"  +
												"`filename`," 	  +
												"`path`," 		  +
												"`filesize`," 	  +
												"`mime_type`" 	  +
											") VALUES " +
											"(" +
												"'" + md5.asHex()	 	+ "'," +
												"'" + url + filename	+ "'," +
												"'" + type 			 	+ "'," +
												  	  user_id 			+ " ," +
												"'" + item.getName() 	+ "'," +
												"'" + path			 	+ "'," +
												uploadFile.length() 	+ " ," +
												"'" + MimeUtil.getMostSpecificMimeType(mimeTypes).toString() + "'" +
											")";
					statement.execute(insertFileQuery);
					stream.close();
					fout.close();
				}
			}
		} catch (FileUploadException e) {
			LOG.error(e);
			throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
		} catch (IOException e) {
			if((e instanceof MalformedStreamException)){
				LOG.error(e + "\n" + "aborted Upload => canceled User?");
			}else {
				LOG.error(e);
				throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
			}
			e.printStackTrace();
		} catch (SQLException e) {
			LOG.error(e + "\n" + insertFileQuery);
			throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
		}finally {
			ConnectionHelper.close(statement);
			ConnectionHelper.close(connection);
		}

		return url + filename;
	}
	
}
